#include "SInput.h"

namespace poly
{
	SInput::SInput(HINSTANCE hInst, HWND hWnd) :
		m_hInstance( hInst ), m_hWnd( hWnd )
	{
		HRESULT hr;
		hr = DirectInput8Create( hInst, DIRECTINPUT_VERSION,
			IID_IDirectInput8, (void**)&m_dinput, 0 );
		assert( hr == DI_OK );

		hr = m_dinput->CreateDevice( GUID_SysMouse, &m_mouse, 0 );
		assert( hr == DI_OK );

		m_mouse->SetDataFormat( &c_dfDIMouse );
		m_mouse->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND );
		hr = m_mouse->Acquire();
		assert( hr == DI_OK );

		hr = m_dinput->CreateDevice( GUID_SysKeyboard, &m_keyboard, 0 );
		assert( hr == DI_OK );

		m_keyboard->SetDataFormat( &c_dfDIKeyboard );
		m_keyboard->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND );
		hr = m_keyboard->Acquire();
		assert( hr == DI_OK );
	}

	SInput::~SInput()
	{
		SAFE_RELEASE( m_dinput );
		SAFE_RELEASE( m_mouse );
		SAFE_RELEASE( m_keyboard );

		for( vector<DIDevice>::iterator joystick = m_joysticks.begin();
			joystick != m_joysticks.end();
			joystick++ )
		{
			SAFE_RELEASE( *joystick );
		}
	}

	SInput::MOUSESTATE SInput::mouseState()
	{
		MOUSESTATE mouseState;
		HRESULT hr = m_mouse->GetDeviceState( sizeof(MOUSESTATE), &mouseState );
		assert( hr == DI_OK );

		return mouseState;
	}

	SInput::KEYSTATE SInput::keyState()
	{
		KEYSTATE keyState;
		HRESULT hr = m_keyboard->GetDeviceState( sizeof(KEYSTATE), &keyState );
		assert( hr == DI_OK );

		return keyState;
	}

	SInput::JOYSTATE SInput::joyState(uint32 joyIndex)
	{
		JOYSTATE joyState;
		HRESULT hr = m_joysticks[joyIndex]->GetDeviceState( sizeof(JOYSTATE), &joyState );
		assert( hr == DI_OK );

		return joyState;
	}

	// used in callback communication
	struct JOYENUM_DATA
	{
		SInput * input;
		SInput::JoystickEnumerator * enumerator;
	};

	void SInput::enumJoysticks(JoystickEnumerator & jEnumerator)
	{
		JOYENUM_DATA data = {this, &jEnumerator};

		m_dinput->EnumDevices( DI8DEVCLASS_GAMECTRL,
			SInput::DIJoystickEnumDevCallback, (void*) &data, DIEDFL_ATTACHEDONLY );
	}

	struct OBJENUM_DATA
	{
		SInput::JoystickEnumerator * enumerator;
		DIDevice joystick;
		uint32 joyIndex;
	};
	
	BOOL CALLBACK SInput::DIJoystickEnumDevCallback( LPCDIDEVICEINSTANCE deviceInstance, LPVOID pRef )
	{
		JOYENUM_DATA * data = (JOYENUM_DATA *) pRef;
		SInput* input = data->input;
		SInput::JoystickEnumerator * enumerator = data->enumerator;
		uint32 joyIndex = input->m_joysticks.size();

		bool create;
		DIDevice joystick;
		uint32 cooperativeLevel = 0;
		uint32 objEnumFlags = 0;

		bool next = enumerator->handleJoystick( create, cooperativeLevel, objEnumFlags, 
			deviceInstance, joyIndex );

		if( !create )
			return next?DIENUM_CONTINUE:DIENUM_STOP;

		HRESULT hr = input->m_dinput->CreateDevice( deviceInstance->guidInstance, &joystick, 0 );
		assert( hr == DI_OK );

		joystick->SetCooperativeLevel( input->m_hWnd, cooperativeLevel );
		joystick->SetDataFormat( &c_dfDIJoystick );
		hr = joystick->Acquire();
		assert( hr == DI_OK );

		OBJENUM_DATA objdata = {enumerator, joystick, joyIndex};
		if( objEnumFlags != 0 )
			joystick->EnumObjects( DIEnumDeviceObjectsCallback,
				(void*) &objdata, objEnumFlags );

		return next?DIENUM_CONTINUE:DIENUM_STOP;
	}

	BOOL CALLBACK SInput::DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE objectInstance, LPVOID pRef)
	{
		OBJENUM_DATA * objdata = (OBJENUM_DATA*) pRef;
		JoystickEnumerator* enumerator = objdata->enumerator;
		DIDevice joystick = objdata->joystick;
		uint32 joyIndex = objdata->joyIndex;
		return enumerator->handleJoystickObject( objectInstance, joystick, joyIndex );
	}
}
